/*
  This file is part of 'Trivial chess'.

  'Trivial chess' is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  'Trivial chess' is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with 'Trivial chess'.  If not, see <http://www.gnu.org/licenses/>.
*/

/* SVN FILE: $Id$ */
/**
 * @author         $Author$
 * @version        $Rev$
 * @lastrevision   $Date$
 * @filesource     $URL$
 */

#ifndef MOVEGENERATORSQ_H_
#define MOVEGENERATORSQ_H_

#include <boost/iterator/iterator_facade.hpp>

#include "MoveGeneratorAbstract.h"

template<class Br, class Mv>
class MoveGeneratorSq
	: public MoveGeneratorAbstract<MoveGeneratorSq<Br,Mv>,Br,Mv> {

public:
	typedef Br Board;
	typedef Mv Move;
public:
	MoveGeneratorSq() : move(Move::NIL()) {}

	MoveGeneratorSq(const Board& board)
		: board(&board),
		  boardMen(BoardMenColor::begin(board, board.sideToMove())) {

		while ( !this->setNewAttack() ) {
			++this->boardMen;
			if ( this->boardMen == this->boardMen.end() ) {
				*this = this->end();
				break;
			}
		}

	}

	void increment() {
		switch ( this->i ) {
		case SLIDER:
			this->incrementAttack(this->sliderAttack);
			break;
		case LEAPER:
			this->incrementAttack(this->leaperAttack);
			break;
		case PAWN:
			this->incrementAttack(this->pawnMove);
			break;
		}


	}

	bool equal(MoveGeneratorSq const& other) const {
		return this->move == other.move;
	}

	const Move dereference() const {
		return this->move;
	}
protected:
	template<class Attack>
	void incrementAttack(Attack& a) {
		if ( a.end() == ++a ) {
			if ( !this->incrementBoard() )
				*this = this->end();
			else {
				switch ( this->i ) {
				case SLIDER:
					this->move = Move(*this->boardMen, *this->sliderAttack);
					break;
				case LEAPER:
					this->move = Move(*this->boardMen, *this->leaperAttack);
					break;
				case PAWN:
					this->move = Move(*this->boardMen, *this->pawnMove);
					break;
				}
			}
		}
		else
			this->move = Move(*this->boardMen, *a);
	}

	bool incrementBoard() {
		do {
			++this->boardMen;
			if ( this->boardMen == this->boardMen.end() )
				return false;
		} while( !this->setNewAttack() );

		return true;
	}

	bool setNewAttack() {
		const Man man = (*this->board)[*this->boardMen];
		if ( man.isPawn() ) {
			this->i = this->PAWN;
			this->pawnMove = PawnMove::begin(*this->board, *this->boardMen);
			this->move = Move(*this->boardMen, *this->pawnMove);
			return this->pawnMove != this->pawnMove.end();
		}
		else if ( man.isSlider() ) {
			this->i = this->SLIDER;
			this->sliderAttack = SliderAttack::begin(*this->board, *this->boardMen);
			this->move = Move(*this->boardMen, *this->sliderAttack);
			return this->sliderAttack != this->sliderAttack.end();
		}
		else {
			this->i = this->LEAPER;
			this->leaperAttack = LeaperAttack::begin(*this->board, *this->boardMen);
			this->move = Move(*this->boardMen, *this->leaperAttack);
			return this->leaperAttack != this->leaperAttack.end();
		}
	}
protected:
	const Board* board;
	BoardMenColor boardMen;
	Move move;
	enum { SLIDER, LEAPER, PAWN } i;

	SliderAttack sliderAttack;
	LeaperAttack leaperAttack;
	PawnMove pawnMove;

};

#endif /* MOVEGENERATORSQ_H_ */
